# -*- coding: utf-8 -*-
# @Time    : 2023/5/3 19:50
# @Author  : wkaanig
# @File    : utils
# @Email   : 15670813638@163.com
# @Software: PyCharm
import base64
import binascii
import hashlib
import io
import json
import math
import os
import platform
import random
import re
import time
import uuid
from pathlib import Path
from typing import List

import cv2
import execjs
import matplotlib.pyplot as plt
import numpy as np
import oss2
import requests
import rsa
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from PIL import Image
from loguru import logger
from requests.exceptions import RequestException

from utils.geetest_utils.save_captcha import CaptchaSave

STATIC_URL = "https://static.geetest.com"


class GTEncrypt:
    def __init__(self):
        self.public_key = "00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81"
        self.public_key_mode = "10001"

    def encrypt_w(self, input_data: dict):
        text = json.dumps(input_data, separators=(",", ":"))
        sec_key = self.create_secret_key(8)
        # rsa 不对称性对 aes的密钥进行加密
        enc_sec_key = self.rsa_encrypt(sec_key, self.public_key, self.public_key_mode)

        # aes 对称加密  进行轨迹加密
        iv = b"0000000000000000"
        enc_text = self.aes_encrypt(text, sec_key.decode("utf-8"), iv)
        w = enc_text + enc_sec_key
        return w

    @staticmethod
    def aes_encrypt(text, key, iv, style="pkcs7"):
        """
        :param text: 文本
        :param key: 密钥
        :param iv: 初始iv  bytes
        :param style: 返回函数类型
        :return:
        """
        encryptor = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv)
        pad_pkcs7 = pad(text.encode("utf-8"), AES.block_size, style=style)
        ciphertext = encryptor.encrypt(pad_pkcs7)
        ret = ciphertext.hex()
        return ret

    def rsa_encrypt(self, text: bytes, n=None, e=None):
        """
        对text 进行rsa加密
        """
        if n is None:
            n = self.public_key
        if e is None:
            e = self.public_key_mode
        PublicKey = rsa.PublicKey(int(n, 16), int(e, 16))  # rsa库公钥形式
        rs = rsa.encrypt(text, PublicKey)
        return rs.hex()

    @staticmethod
    def create_secret_key(size):
        # 作用是返回的二进制数据的十六进制表示。每一个字节的数据转换成相应的2位十六进制表示
        return binascii.hexlify(os.urandom(size))


class GTTrace:
    @staticmethod
    def get_trace_normal_old(distance):
        def sigmoid(x, b):
            t = 8

            s = 2 / (2 + math.exp(-x + t))
            return s * b

        global current_x
        track = [[random.randint(-30, -19), random.randint(-25, -20), 0], [0, 0, 0]]
        x = 0
        x_list = []
        while True:
            x = x + random.randint(1, 4)

            if x < distance:
                x_list.append(x)
            else:
                break
        x = [(10 / 20) * i for i in x_list]
        _y = random.randint(-1, 1)
        current_t = random.randint(-40, -30)
        for _x in x:
            current_x = int(sigmoid(_x, distance))
            _t = random.randint(40, 60)
            current_t += _t
            track.append([current_x, _y, current_t])
        track.append([current_x, _y, current_t + random.randint(100, 200)])
        return track


class GTImgProcess:

    @staticmethod
    def get_target(img_bytes: bytes = None):
        image = Image.open(io.BytesIO(img_bytes))
        w, h = image.size
        starttx = 0
        startty = 0
        end_x = 0
        end_y = 0
        for x in range(w):
            for y in range(h):
                p = image.getpixel((x, y))
                if p[-1] == 0:
                    if startty != 0 and end_y == 0:
                        end_y = y

                    if starttx != 0 and end_x == 0:
                        end_x = x
                else:
                    if startty == 0:
                        startty = y
                        end_y = 0
                    else:
                        if y < startty:
                            startty = y
                            end_y = 0
            if starttx == 0 and startty != 0:
                starttx = x
            if end_y != 0:
                end_x = x
        return image.crop((starttx, startty, end_x, end_y)), starttx, startty

    def slide_match(
            self,
            target_bytes: bytes = None,
            background_bytes: bytes = None,
            simple_target: bool = False,
            flag: bool = False,
    ):
        if not simple_target:
            try:
                target, target_x, target_y = self.get_target(target_bytes)
                target = cv2.cvtColor(np.asarray(target), cv2.IMREAD_ANYCOLOR)
            except SystemError as e:
                # SystemError: tile cannot extend outside image
                if flag:
                    raise e
                return self.slide_match(
                    target_bytes=target_bytes, background_bytes=background_bytes, simple_target=True, flag=True
                )
        else:
            target = cv2.imdecode(np.frombuffer(target_bytes, np.uint8), cv2.IMREAD_ANYCOLOR)
            target_y = 0
            target_x = 0

        background = cv2.imdecode(np.frombuffer(background_bytes, np.uint8), cv2.IMREAD_ANYCOLOR)

        background = cv2.Canny(background, 100, 200)
        target = cv2.Canny(target, 100, 200)

        background = cv2.cvtColor(background, cv2.COLOR_GRAY2RGB)
        target = cv2.cvtColor(target, cv2.COLOR_GRAY2RGB)

        res = cv2.matchTemplate(background, target, cv2.TM_CCOEFF_NORMED)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        h, w = target.shape[:2]
        bottom_right = (max_loc[0] + w, max_loc[1] + h)
        return {
            "target_y": target_y,
            "target": [int(max_loc[0]), int(max_loc[1]), int(bottom_right[0]), int(bottom_right[1])],
        }


class GtClickCrack:
    """显示图片, 双击返回坐标, ESC 退出并返回结果"""

    def __init__(self, bg: bytes or str, small_imgs: bytes or str or List[bytes] or List[str] = None, save=False):
        """

        :param bg: 底图
        :param small_imgs: 小图 (列表) 用于显示点击顺序, 可不传
        """
        self.upload_data = None
        self.bg_url = bg if isinstance(bg, str) else None
        self.small_imgs_url = None
        if isinstance(small_imgs, list):
            if small_imgs and isinstance(small_imgs[0], str):
                self.small_imgs_url = small_imgs
                small_imgs = [
                    requests.get(i, headers={
                        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
                    }).content
                    for i in small_imgs
                ]
        else:
            if isinstance(small_imgs, str):
                self.small_imgs_url = [small_imgs]
                small_imgs = requests.get(small_imgs, headers={
                    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
                }).content
                small_imgs = [small_imgs]
        self.bg = bg if isinstance(bg, bytes) else requests.get(bg, headers={
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
        }).content
        self.save = save
        self.small_imgs = [small_imgs] if small_imgs and isinstance(small_imgs, bytes) else small_imgs
        self.points = []

    def add_point(self, event, x, y, *args, **kwargs):
        if event == cv2.EVENT_LBUTTONUP:
            self.points.append((x, y))
            logger.info(f"当前点击: {(x, y)}")

    def discern(self):
        uk_md5 = self.bg_url.split('/')[-1].split('.')[0]
        history_point = CaptchaSave().get_points_by_history(uk_md5)
        if history_point:
            return history_point
        small_img_list = []
        if len(self.small_imgs) == 3:
            if self.bg_url and "word" in self.bg_url:
                cap_type = "word"
            else:
                cap_type = "icon"
        elif len(self.small_imgs) == 1:
            cap_type = "nine"
        else:
            cap_type = "phrase"
        if self.small_imgs:
            for i, img in enumerate(self.small_imgs):
                img = cv2.imdecode((np.frombuffer(img, np.uint8)), cv2.IMREAD_UNCHANGED)
                img = cv2.resize(img, (50, 50))
                small_img_list.append(img)
        if small_img_list:
            temp_img = np.hstack(small_img_list)
            plt.imshow(temp_img)
            plt.show()
        logger.info("开始点击, 双击确定坐标, 点击完毕后按 ESC 退出")
        cv2.namedWindow("image", cv2.WINDOW_NORMAL)
        cv2.setMouseCallback("image", self.add_point)
        bg_obj = cv2.imdecode((np.frombuffer(self.bg, np.uint8)), cv2.IMREAD_UNCHANGED)
        cv2.imshow("image", bg_obj)
        while True:
            if cv2.waitKey(20) & 0xFF == 27:  # 按ESC退出
                break
        cv2.destroyAllWindows()
        self.upload_data = {
            "background": self.bg_url,
            "question": self.small_imgs_url,
            "answer": self.points,
            "uk_md5": self.bg_url.split('/')[-1].split('.')[0],
            "cap_type": cap_type,
            "version": "v4",
        }
        return self.points


class GTThirdPartyClick:
    TUTENGOCR_KEY = "9HVNNpGWsWjt9jof8j95qphrGd"
    TUTENGOCR_KEY = "4PbIenPThg7Kl9J3XrxZ9FA4sO"

    def __init__(self, bg: bytes, small_imgs: bytes or List[bytes] = None, captcha_type=None):
        """

        :param bg: 底图
        :param small_imgs: 小图 (列表) 用于显示点击顺序, 可不传
        """
        self.bg_url = bg if isinstance(bg, str) else None
        self.small_imgs_url = None
        if isinstance(small_imgs, list):
            if small_imgs and isinstance(small_imgs[0], str):
                self.small_imgs_url = small_imgs
                small_imgs = [
                    requests.get(i, headers={
                        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
                    }).content
                    for i in small_imgs]
        else:
            if isinstance(small_imgs, str):
                self.small_imgs_url = [small_imgs]
                small_imgs = requests.get(small_imgs, headers={
                    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
                }).content
                small_imgs = [small_imgs]
        self.cache_path_name = None
        self.bg = bg if isinstance(bg, bytes) else requests.get(bg, headers={
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
        }).content
        self.captcha_type = captcha_type
        self.small_imgs = [small_imgs] if small_imgs and isinstance(small_imgs, bytes) else small_imgs
        self.points = []
        self.upload_data = None

    def discern(self):
        uk_md5 = self.bg_url.split('/')[-1].split('.')[0]
        history_point = CaptchaSave().get_points_by_history(uk_md5)
        if history_point:
            return history_point
        params = {"key": self.TUTENGOCR_KEY}
        if len(self.small_imgs) == 3:
            if self.captcha_type == "word":
                cap_type = "word"
                url = "https://www.tutengai.com/api/captcha04/zh_sim"
                data = {
                    "image": base64.b64encode(self.bg).decode(),
                    "title": [base64.b64encode(i).decode() for i in self.small_imgs],
                }
            else:
                cap_type = "icon"
                url = "https://www.tutengai.com/api/captcha04/icon"
                data = {
                    "image": base64.b64encode(self.bg).decode(),
                    "title": [base64.b64encode(i).decode() for i in self.small_imgs],
                }
        elif len(self.small_imgs) == 1:
            cap_type = "nine"
            url = "https://www.tutengai.com/api/icon/3032"
            data = {
                "image": base64.b64encode(self.bg).decode(),
                "title": base64.b64encode(self.small_imgs[0]).decode(),
            }
        else:
            cap_type = "phrase"
            url = "https://www.tutengai.com/api/captcha04/order"
            data = {
                "image": base64.b64encode(self.bg).decode(),
            }
        res = requests.get(url, params=params, json=data).json()
        point_str = res["data"]
        if cap_type in ['icon', 'word', 'phrase']:
            if res["code"] != 1:
                raise ValueError(f"图腾打码出现错误，错误：{res}")
        else:
            if res["code"] != 0:
                raise ValueError(f"图腾打码出现错误，错误：{res}")
        if len(point_str.split('|')) != 3:
            raise ValueError(f"图腾打码数量出现错误，错误：{res}")
        if cap_type in ["phrase", "nine", "icon", "word"]:
            self.upload_data = {
                "background": self.bg_url,
                "question": self.small_imgs_url,
                "answer": [list(map(int, i.split(","))) for i in point_str.split("|")],
                "uk_md5": self.bg_url.split('/')[-1].split('.')[0],
                "cap_type": cap_type,
                "version": "v4",
            }
        return [list(map(int, i.split(","))) for i in point_str.split("|")]

    @staticmethod
    def upload_file(file_name, file_content):
        oss2.Bucket(
            oss2.Auth("LTAI5tQjVgxe34iE8ZEqdMbB", "3eEIIxQ2eWKGxyz3lLrT2JZOf1Qs26"),
            "http://oss-cn-chengdu.aliyuncs.com",
            "wkaanig-img",
        ).put_object(file_name, file_content)


class Geetest4(GTEncrypt, GTTrace, GTImgProcess):

    def __init__(self, captcha_id="54088bb07d2df3c46b79f80300b0abbe", proxies=None, page_url=None, risk_type=None,
                 click=platform.system() != 'Linux', **kwargs):
        super().__init__()
        if proxies is None:
            proxies = {}
        if page_url is None:
            page_url = "https://gt4.geetest.com/"
        self.risk_type = risk_type
        self.session = requests.Session()
        self.session.proxies.update(proxies)
        headers = {
            "Accept": "*/*",
            "Accept-Language": "zh-CN,zh;q=0.9,zu;q=0.8,be;q=0.7,en;q=0.6",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "Pragma": "no-cache",
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
        }
        if page_url:
            headers['Referer'] = page_url
        self.session.headers = headers
        self.captcha_id = captcha_id
        self.click = click
        self.upload_data = None

    def init(self) -> dict:
        """
        初始化验证码
        {'status': 'success', 'data': {'lot_number': 'd52a05ec33034f3da3de5c7f6c2d3846', 'captcha_type': 'slide', 'js': '/js/gcaptcha4.js', 'css': '/css/gcaptcha4.css', 'static_path': '/v4/static/v1.7.0', 'slice': 'captcha_v4/e70fbf1d77/slide/e7874d46ba/2022-04-21T09/slice/561fef74af7d45998391eeb63f1b5673.png', 'bg': 'captcha_v4/e70fbf1d77/slide/e7874d46ba/2022-04-21T09/bg/561fef74af7d45998391eeb63f1b5673.png', 'ypos': 82, 'gct_path': '/v4/gct/gct4.b7f8c39d695c50a8ecbbe71c9326bc2a.js', 'arrow': 'arrow_1', 'show_voice': False, 'feedback': 'https://www.geetest.com/Helper', 'logo': True, 'pt': '1', 'captcha_mode': 'risk_manage', 'guard': False, 'check_device': True, 'language': 'zh', 'custom_theme': {'_style': 'stereoscopic', '_color': 'hsla(224,98%,66%,1)', '_gradient': 'linear-gradient(180deg, hsla(224, 98%,  71%, 1) 0%, hsla(224,98%,66%,1) 100%)', '_hover': 'linear-gradient(180deg, hsla(224,98%,66%,1) 0%, hsla(224, 98%,  71%, 1) 100%)', '_brightness': 'system', '_radius': '4px'}, 'pow_detail': {'version': '1', 'bits': 0, 'datetime': '2023-05-03T20:41:48.665468+08:00', 'hashfunc': 'md5'}, 'payload': '39Gl86BQgpTxeuyf4iPX01DGX3TpqGEuX-LaPoCT_TFk3988jKtpBaTMoEIfAn03alRL_DFZBFfQMcHy3KaPT7iGzfpBSZCEOfg7AsAacU8TvqN3n_DCNMfXXuWBSp5T9QUmLKJUXd50G_bHTu1YAaDG-aBBqx91UVmWoUchJP4LUTtghMtqTG9HfBRAI0FEDVaJbV9f7YocStkzygjN3sRlUtlgeVR6DcSQLwf0axZ_cERYTN9tuMni-vRaZprgr_9REVsd89w761HMSerymYry2NA5kIr2Bp05p7pGRfjmOyf9HuqVUxAF1ZNqYwHr9VOza3DK18n2NQg3g4MEJPlQ4gb_zOq-fEY9N2VXWT6pqAS5QTuuLXyHhH0OYRyInueVXXxjHKWxTGgz1wOj1r0tBD0SKxlyncIvAFDRNrsZIVXOOdeFfWnoT2juTaEQet5LyrN8cIbA-ZfQYEsRRuX5IgQZowrqfjXB2VBxd4rRFRTcZstuj_6FehrncklyY_4v3Yx5SzaTTaTCEx27CtavZFME_0-_EQH4qEggvfz7cklQMKYoM_cPwC0KHcd2fePxFEazcr_9cxFY_9oK36rhxQhGoFGteq87LmB-c__dg20k1_nP9hCcUD4oBUkeNOLw5BJ-7LjPwNLRLeklTw==', 'process_token': '11f1bea0d788750cf9f72511de8d4df53008efdadcf47a6ab0d9d1dea38f6527', 'payload_protocol': 1}}
        :return:
        """
        params = {
            "captcha_id": self.captcha_id,
            "challenge": uuid.uuid4(),
            "client_type": "web",
            "lang": "zho",
            "callback": f"geetest_{round(time.time() * 1000)}",
        }
        if self.risk_type:
            params['risk_type'] = self.risk_type
        url: str = "https://gcaptcha4.geetest.com/load"
        response = self.session.get(url, params=params, timeout=10)
        data = self._format_response(response.text)
        logger.debug(f"{data}")
        return data

    def verify_continue(self, init_data=None, continue_data=None):
        init_data = init_data.get("data")
        params = {
            "callback": f"geetest_{round(time.time() * 1000)}",
            "captcha_id": self.captcha_id,
            "client_type": "web",
            "lot_number": continue_data["lot_number"],
            "pt": init_data["pt"],
            "lang": init_data["language"],
            "payload": continue_data["payload"],
            "process_token": continue_data["process_token"],
            "payload_protocol": continue_data["payload_protocol"]
        }
        url: str = "https://gcaptcha4.geetest.com/load"
        response = self.session.get(url, params=params, timeout=10)
        data = self._format_response(response.text)
        logger.debug(f"verify_continue {data}")
        return self.verify(data, is_continue=False)  # 只重试一次

    def verify(self, init_data=None, is_continue=True):
        url = "https://gcaptcha4.geetest.com/verify"
        if init_data is None:
            init_data = self.init()
        if init_data["data"]["captcha_type"] in ["word", "nine", "icon", "phrase", "slide", "ai"]:
            logger.info(f'captcha_type：{init_data["data"]["captcha_type"]} 资源加载中...')
            params = {
                "callback": f"geetest_{round(time.time() * 1000)}",
                "captcha_id": self.captcha_id,
                "client_type": "web",
                "lot_number": init_data["data"]["lot_number"],
                "risk_type": init_data["data"]["captcha_type"],
                "payload": init_data["data"]["payload"],
                "process_token": init_data["data"]["process_token"],
                "payload_protocol": init_data["data"]["payload_protocol"],
                "pt": init_data["data"]["pt"],
                "w": self._gen_payload(init_data),
            }
            time.sleep(random.uniform(0.1, 0.6))
            response = self.session.get(url, params=params)
            ret = self._format_response(response.text)
            if (ret.get("data", {}).get("result") or "") == "success":
                logger.success(ret.get("data"))
                if self.upload_data:
                    CaptchaSave().upload_captcha_to_mysql(self.upload_data)
            else:
                if is_continue and (ret.get("data", {}).get("result") or "") == "continue":
                    return self.verify_continue(init_data=init_data, continue_data=ret.get("data"))
                logger.error(ret.get("data"))
                self.upload_error(init_data["data"]["imgs"], init_data["data"].get("ques") or [], init_data["data"]["captcha_type"])
            return ret.get("data")

    def test(self):
        success = 0
        for _ in range(100):
            try:
                ret = self.verify()
                if ret.get("result") == "success":
                    success += 1
            except Exception as e:
                logger.exception(e)
            logger.info(f"成功率 {success}/{_ + 1}")

    def _get_pow(self, init_data):
        pow_detail = init_data["data"]["pow_detail"]
        captcha_id = self.captcha_id
        lot_number = init_data["data"]["lot_number"]
        n = pow_detail["hashfunc"]
        i = pow_detail["version"]
        r = pow_detail["bits"]
        s = pow_detail["datetime"]
        a = r % 4
        u = r // 4
        c = "0" * u
        _ = f"{i}|{r}|{n}|{s}|{captcha_id}|{lot_number}||"

        while True:
            h = "".join(random.choices("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", k=16))
            p = getattr(hashlib, n)((_ + h).encode()).hexdigest()
            if a == 0:
                if p.startswith(c):
                    return {"pow_msg": _ + h, "pow_sign": p}
            else:
                if p.startswith(c):
                    d = p[u]
                    if d.isnumeric():
                        if int(d) <= 7 // a:
                            return {"pow_msg": _ + h, "pow_sign": p}

    def _get_distance(self, init_data):
        bg_url = STATIC_URL + "/" + init_data["data"]["bg"]
        slice_url = STATIC_URL + "/" + init_data["data"]["slice"]
        target_bytes = self.session.get(slice_url).content
        background_bytes = self.session.get(bg_url).content
        res = self.slide_match(target_bytes, background_bytes, simple_target=True)
        return res["target"][0]

    def _gen_payload(self, init_data):
        options = init_data["data"]
        gct_input = {
            "geetest": "captcha",
            "lang": options["language"],
            "ep": "123",
        }
        if init_data["data"]["captcha_type"] == "slide":
            distance = self._get_distance(init_data)
            track = self.get_trace_normal_old(distance)
            pass_time = track[-1][2]
            data = {
                "setLeft": distance,  # a
                "track": track,
                "passtime": pass_time,
                "userresponse": distance / 1.0059466666666665 + 2,  # a / 1.0059466666666665 + 2
                "device_id": hashlib.md5(f"{time.time() * 1000 - 20000}".encode("utf-8")).hexdigest(),  # canvas md5
                "lot_number": options["lot_number"],
            }
        elif init_data["data"]["captcha_type"] in ["word", "nine", "icon", "phrase"]:
            point_list = self._get_point(init_data)
            data = {
                "passtime": random.randint(1000, 2500),
                "userresponse": point_list,
                "device_id": hashlib.md5(f"{time.time() * 1000 - 20000}".encode("utf-8")).hexdigest(),  # canvas md5
                "lot_number": options["lot_number"],
            }
        elif init_data["data"]["captcha_type"] == "ai":
            data = {
                "device_id": hashlib.md5(f"{time.time() * 1000 - 20000}".encode("utf-8")).hexdigest(),  # canvas md5
                "lot_number": options["lot_number"],
            }
        else:
            raise Exception(f'新的验证类型 {init_data["data"]["captcha_type"]}')
        data.update(self._get_pow(init_data))
        gct_input = self._gct(gct4_url=STATIC_URL + options["gct_path"], obj=gct_input)
        data.update(gct_input)
        data.update({"em": {"ph": 0, "cp": 0, "ek": "11", "wd": 1, "nt": 0, "si": 0, "sc": 0}})
        # logger.debug(json.dumps(data, indent=4))
        return self.encrypt_w(input_data=data)

    @staticmethod
    def _format_response(text):
        try:
            data = json.loads(re.sub(r".*?\d+\(", "", text.strip(")")))
        except Exception as e:
            raise ValueError(f"{e} {text}")
        return data

    def cache_js(self, js_url):
        cache_js = Path(f"/tmp/{js_url.split('/')[-1]}")
        if cache_js.exists():
            try:
                with open(cache_js, "r") as f:
                    return f.read()
            except IOError as e:
                logger.error(f"Failed to read cached JavaScript file: {e}")
                return None
        else:
            try:
                cache_js.parent.mkdir(parents=True, exist_ok=True)
                file_io = self.session.get(js_url).content.decode("utf-8")
                with open(cache_js, "w") as f:
                    f.write(file_io)
            except (RequestException, IOError) as e:
                logger.error(f"Failed to cache JavaScript file: {e}")
                return None
        return file_io

    def _gct(self, gct4_url, obj):
        js_code = (
                "exports = undefined;module = undefined;"
                + self.cache_js(js_url=gct4_url)
                + "function get_gct(obj){_gct(obj);return obj}"
        )
        return execjs.compile(js_code).call("get_gct", obj)

    def _get_point(self, init_data):
        images_objs = []
        images_objs_url = []
        for path in init_data["data"].get("ques", []):
            img_url = STATIC_URL + "/" + path
            images_objs_url.append(img_url)
            # images_objs.append(self.session.get(img_url).content)
        # bg = self.session.get(STATIC_URL + "/" + init_data["data"]["imgs"]).content
        bg_url = STATIC_URL + "/" + init_data["data"]["imgs"]
        points = self._image_to_point(bg=bg_url, small_images=images_objs_url, captcha_type=init_data["data"]["captcha_type"])
        if init_data["data"]["captcha_type"] == "nine":
            points = [[int(i[1] // 90) + 1, int(i[0] // 90) + 1] for i in points]
        else:
            points = [[int(i[0] / 300 * 10000), int(i[1] / 200 * 10000)] for i in points]
        logger.info("点击坐标 {}".format(points))
        return points

    def _image_to_point(self, bg, small_images, captcha_type=None):
        if self.click:
            crack_cls = GtClickCrack(bg=bg, small_imgs=small_images, save=False)
            res = crack_cls.discern()
            self.upload_data = crack_cls.upload_data
            return res
        else:
            crack_cls = GTThirdPartyClick(bg=bg, small_imgs=small_images, captcha_type=captcha_type)
            res = crack_cls.discern()
            self.upload_data = crack_cls.upload_data
            return res

    @staticmethod
    def change_rgba_to_rgb(img, input_width=None, input_height=None):
        if input_width is None:
            input_width = img.shape[0]
        if input_height is None:
            input_height = img.shape[1]
        img = cv2.resize(img, (input_width, input_height))
        if img.shape[2] == 4:
            img[np.where(img[:, :, 3] == 0)] = [255, 255, 255, 255]
            # 将RGBA图像转换为RGB图像
            img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
        return img

    def upload_error(self, pic_url, pic_que, pic_type):
        pass
        # pic_url = STATIC_URL + "/" + pic_url
        # pic_que = [(STATIC_URL + "/" + i) for i in pic_que]
        # logger.error(f"upload error, pic_type {pic_type}, pic_url {pic_url} pic_que {pic_que} ")
        # oss_client = oss2.Bucket(
        #     oss2.Auth("LTAI5tQjVgxe34iE8ZEqdMbB", "3eEIIxQ2eWKGxyz3lLrT2JZOf1Qs26"),
        #     "http://oss-cn-chengdu.aliyuncs.com",
        #     "wkaanig-img",
        # )
        # img = np.array(Image.open(io.BytesIO(self.session.get(pic_url).content)))
        # image_queue_path = [io.BytesIO(self.session.get(i).content) for i in pic_que]
        # image_queue = [self.change_rgba_to_rgb(np.array(Image.open(i)), 30, 30) for i in image_queue_path]
        # image_queue_merge = np.hstack(image_queue)
        # image_queue_merge = cv2.copyMakeBorder(
        #     image_queue_merge, 0, 0, 0, img.shape[1] - image_queue_merge.shape[1],
        #     cv2.BORDER_CONSTANT, value=[0, 0, 0]
        # )
        # img = np.vstack((img, image_queue_merge))
        # file_name = pic_url.split("/")[-1]
        # oss_client.put_object(f"gt4/sample/{pic_type}/{file_name}", cv2.imencode(".jpg", img)[1].tobytes())


if __name__ == '__main__':
    test = Geetest4(
        "b608ae7850d2e730b89b02a384d6b9cc",
        page_url="https://shiming.gsxt.gov.cn/corp-query-search-1.html/",
        click=True,
        proxies=None,
        # risk_type="word",
        # risk_type="icon",
    )
    print(test.verify())
